package com.raymond.thread.manager;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.raymond.thread.manager.annotation.ManagerCirculationThread;
import com.raymond.thread.manager.annotation.ManagerScheduledThread;
import com.raymond.thread.manager.annotation.ManagerThread;
import com.raymond.thread.manager.bean.BaseThreadDefinition;
import com.raymond.thread.manager.bean.CirculationThreadDefinition;
import com.raymond.thread.manager.bean.ScheduledThreadDefinition;
import com.raymond.thread.manager.circulation.BaseCirculationThread;
import com.raymond.thread.manager.pool.DefaultScheduledThreadPool;
import com.raymond.thread.manager.pool.DefaultThreadPool;
import com.raymond.thread.manager.scheduled.BaseScheduledThread;
import com.raymond.thread.manager.scheduled.ScheduleTaskUtils;
import com.raymond.thread.manager.utils.ManagerThreadUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * 管理线程运行
 *
 * @author :  raymond
 * @version :  V1.0
 * @date :  2020-09-12 18:55
 */
@Component
public class ThreadManager extends Thread {
    private static final Logger logger = LoggerFactory.getLogger(ThreadManager.class);

    /**
     * 线程管理启动
     */
    @Override
    public void run() {
        try {
            Map<String, BaseThread> beansOfType = SpringContextUtil.getBeansOfType(BaseThread.class);
            Set<BaseThreadDefinition> managerThreadClass = getManagerThreadClass(beansOfType);
            runThread(getOrder(managerThreadClass));
        } catch (Exception e) {
            logger.error("启动程序异常，程序退出", e);
            System.exit(1);
        }

    }


    
    /**
     * 将线程定义排序
     * @param managerThreadClass 线程定义集合
     * @return 排序好的线程对象
     */
    private List<BaseThreadDefinition> getOrder(Collection<BaseThreadDefinition> managerThreadClass) {
        List<BaseThreadDefinition> baseThreadDefinitions = new ArrayList<>(managerThreadClass);
        baseThreadDefinitions.sort(Comparator.comparingInt(BaseThreadDefinition::getOrder));
        return baseThreadDefinitions;
    }

    /**
     * 运行多个线程
     * @param managerThreadClass 需要管理的线程集合
     */
    private void runThread(List<BaseThreadDefinition> managerThreadClass) {
        managerThreadClass.forEach(this::runThread);
    }

    /**
     * 运行单个线程
     * @param managerThreadBean 需要运行的线程
     */
    private void runThread(BaseThreadDefinition managerThreadBean) {
        if (CirculationThreadDefinition.class.isAssignableFrom(managerThreadBean.getClass())) {
            CirculationThreadDefinition circulationThreadBean = (CirculationThreadDefinition) managerThreadBean;
            runCirculationThread(circulationThreadBean);
            return;
        }
        if (ScheduledThreadDefinition.class.isAssignableFrom(managerThreadBean.getClass())) {
            ScheduledThreadDefinition scheduledThreadBean = (ScheduledThreadDefinition) managerThreadBean;
            runScheduledThread(scheduledThreadBean);
            return;
        }
        ThreadPoolTaskExecutor threadPoolTaskExecutor = SpringContextUtil.getBean(managerThreadBean.getThreadPoolName(), ThreadPoolTaskExecutor.class);
        BaseThread baseThread = SpringContextUtil.getBean(managerThreadBean.getBaseThread());
        baseThread.setName(managerThreadBean.getThreadName());
        threadPoolTaskExecutor.execute(baseThread);
        ManagerThreadUtil.putBaseThread(baseThread);
    }

    /**
     * 运行定时线程
     * @param scheduledThreadBean 需要运行的定时线程信息
     */
    private void runScheduledThread(ScheduledThreadDefinition scheduledThreadBean) {
        ScheduledThreadPoolExecutor threadPoolTaskExecutor = SpringContextUtil.getBean(scheduledThreadBean.getThreadPoolName(), ScheduledThreadPoolExecutor.class);
        BaseThread baseThread = SpringContextUtil.getBean(scheduledThreadBean.getBaseThread());
        baseThread.setName(scheduledThreadBean.getThreadName());
        String corn = getValue(scheduledThreadBean.getCorn());
        ManagerThreadUtil.putBaseThread(baseThread);
        ScheduledFuture<?> scheduledFuture;
        if (StrUtil.isNotEmpty(corn)) {
            //去掉Spring的cron表达式定时线程
            ScheduleTaskUtils taskUtils = SpringContextUtil.getBean(ScheduleTaskUtils.class);
            scheduledFuture = taskUtils.addTask(baseThread, corn);
//            scheduledFuture = threadPoolTaskExecutor.scheduleAtFixedRate(baseThread, getCronInitialDelay(corn),
//                    StaticValues.DAY_MILLI_SECONDS, TimeUnit.MILLISECONDS);
        } else if (scheduledThreadBean.getStrategy() == ScheduledThreadDefinition.Strategy.AT_FIXED_RATE) {
            scheduledFuture = threadPoolTaskExecutor.scheduleAtFixedRate(baseThread, scheduledThreadBean.getInitialDelay(),
                    scheduledThreadBean.getPeriod(), TimeUnit.MILLISECONDS);
        } else {
            scheduledFuture = threadPoolTaskExecutor.scheduleWithFixedDelay(baseThread, scheduledThreadBean.getInitialDelay(),
                    scheduledThreadBean.getPeriod(), TimeUnit.MILLISECONDS);
        }
        ManagerThreadUtil.putScheduledThread(baseThread.getName(), scheduledFuture);
    }

    /**
     * 运行循环线程
     * @param circulationThreadBean 需要运行的循环线程信息
     */
    private void runCirculationThread(CirculationThreadDefinition circulationThreadBean) {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = SpringContextUtil.getBean(circulationThreadBean.getThreadPoolName(), ThreadPoolTaskExecutor.class);
        int threadNum = circulationThreadBean.getThreadNum();
        for (int i = 0; i < threadNum; i++) {
            BaseThread baseThread = SpringContextUtil.getBean(circulationThreadBean.getBaseThread());
            baseThread.setName(circulationThreadBean.getThreadName() + (threadNum == 1 ? "" : "-" + i));
            threadPoolTaskExecutor.execute(baseThread);
            ManagerThreadUtil.putBaseThread(baseThread);
        }
    }

    /**
     * 通过容器中的bean获取线程管理的信息
     * @param beansOfType 容器中的bean
     * @return 管理线程的信息
     */
    private Set<BaseThreadDefinition> getManagerThreadClass(Map<String, BaseThread> beansOfType) {
        Set<BaseThreadDefinition> managerThreadBeans = new HashSet<>();
        for (BaseThread baseThread : beansOfType.values()) {
            managerThreadBeans.add(getManagerThreadClass(baseThread));
        }
        return managerThreadBeans;
    }

    /**
     * 判断线程是那种线程并给于初始值
     * 支持循环线程(BaseCirculationThread),定时线程(BaseScheduledThread),基本线程(BaseThread)
     * @param baseThread baseThread
     * @return 线程初始化信息
     */
    @SuppressWarnings("unchecked")
    private BaseThreadDefinition getManagerThreadClass(BaseThread baseThread) {
        Class<? extends BaseThread> aClass = baseThread.getClass();
        if (BaseCirculationThread.class.isAssignableFrom(aClass)) {
            Class<? extends BaseCirculationThread> aClass1 = (Class<? extends BaseCirculationThread>) aClass;
            return getManagerCirculationThreadBean(aClass1);
        }
        if (BaseScheduledThread.class.isAssignableFrom(aClass)) {
            Class<? extends BaseScheduledThread> aClass1 = (Class<? extends BaseScheduledThread>) aClass;
            return getManagerScheduledThreadBean(aClass1);
        }
        return getBaseManagerThreadBean(aClass);
    }

    /**
     * 获取基本线程bean初始化信息,没有注解给默认值,
     * 支持注解@ManagerThread
     * @param baseThread 循环线程bean
     * @return 返回循环线程bean,没有注解给默认值
     */
    private BaseThreadDefinition getBaseManagerThreadBean(Class<? extends BaseThread> baseThread) {
        ManagerThread annotation = baseThread.getAnnotation(ManagerThread.class);
        if (annotation == null) {
            return new BaseThreadDefinition(baseThread, DefaultThreadPool.THREAD_POOL_NAME, DefaultThreadPool.THREAD_NAME);
        }
        String threadPoolName = getThreadPoolName(annotation.threadPoolName());
        String threadName = getThreadName(annotation.threadName());
        return new BaseThreadDefinition(baseThread, threadPoolName, threadName, annotation.order());
    }

    /**
     * 获取定时线程bean初始化信息,没有注解给默认值,
     * 支持注解@ManagerScheduledThread,@ManagerThread
     * @param baseScheduledThread 循环线程bean
     * @return 返回循环线程bean,没有注解给默认值
     */
    private ScheduledThreadDefinition getManagerScheduledThreadBean(Class<? extends BaseScheduledThread> baseScheduledThread) {
        ManagerScheduledThread scheduledThread = baseScheduledThread.getAnnotation(ManagerScheduledThread.class);
        if (scheduledThread != null) {
            String threadPoolName = getScheduledThreadPoolName(scheduledThread.managerThread().threadPoolName());
            String threadName = getThreadName(scheduledThread.managerThread().threadName());
            return new ScheduledThreadDefinition(baseScheduledThread, threadPoolName, threadName, scheduledThread.strategy(),
                    scheduledThread.initialDelay(), scheduledThread.period(), scheduledThread.managerThread().order(), scheduledThread.corn());
        }
        ManagerThread managerThread = baseScheduledThread.getAnnotation(ManagerThread.class);
        if (managerThread != null) {
            String threadPoolName = getScheduledThreadPoolName(managerThread.threadPoolName());
            String threadName = getThreadName(managerThread.threadName());
            return new ScheduledThreadDefinition(baseScheduledThread, threadPoolName, threadName,
                    ScheduledThreadDefinition.Strategy.AT_FIXED_RATE, 1000, 1000, managerThread.order(), "");
        }
        return new ScheduledThreadDefinition(baseScheduledThread, DefaultScheduledThreadPool.SCHEDULED_THREAD_POOL_NAME,
                DefaultThreadPool.THREAD_NAME, ScheduledThreadDefinition.Strategy.AT_FIXED_RATE, 1000, 1000);

    }

    /**
     * 获取循环线程bean初始化信息,没有注解给默认值,
     * 支持注解@ManagerCirculationThread,@ManagerThread
     * @param baseCirculationThread 循环线程bean
     * @return 返回循环线程bean,没有注解给默认值
     */
    private CirculationThreadDefinition getManagerCirculationThreadBean(Class<? extends BaseCirculationThread> baseCirculationThread) {
        ManagerCirculationThread circulationThread = baseCirculationThread.getAnnotation(ManagerCirculationThread.class);
        if (circulationThread != null) {
            int threadNum = getThreadNum(circulationThread.threadNum());
            String threadPoolName = getThreadPoolName(circulationThread.managerThread().threadPoolName());
            String threadName = getThreadName(circulationThread.managerThread().threadName());
            return new CirculationThreadDefinition(baseCirculationThread, threadPoolName, threadName, threadNum, circulationThread.managerThread().order());
        }
        ManagerThread managerThread = baseCirculationThread.getAnnotation(ManagerThread.class);
        if (managerThread != null) {
            String threadPoolName = getThreadPoolName(managerThread.threadPoolName());
            String threadName = getThreadName(managerThread.threadName());
            return new CirculationThreadDefinition(baseCirculationThread, threadPoolName, threadName, 1, managerThread.order());
        }
        return new CirculationThreadDefinition(baseCirculationThread, DefaultThreadPool.THREAD_POOL_NAME, DefaultThreadPool.THREAD_NAME, 1);
    }



    /**
     * 获取当前线程个数
     * @param threadNumStr 注解上的线程数
     * @return 当前线程个数,没有默认1个线程
     */
    private int getThreadNum(String threadNumStr) {
        int threadNum = 1;
        if (isConfiguration(threadNumStr)) {
            String value = SpringContextUtil.getValue(getKey(threadNumStr));
            if (NumberUtil.isNumber(value)) {
                return Integer.parseInt(value);
            }
        }
        if (NumberUtil.isNumber(threadNumStr)) {
            threadNum = Integer.parseInt(threadNumStr);
            if (threadNum < 1) {
                threadNum = 1;
            }
        }
        return threadNum;
    }

    /**
     * 获取定时线程池bean名称
     * @param threadPoolName 注解上的线程池名
     * @return 没有指定线程名使用默认线程池bean
     */
    private String getScheduledThreadPoolName(String threadPoolName) {
        if (StrUtil.isEmpty(threadPoolName)) {
            threadPoolName = DefaultScheduledThreadPool.SCHEDULED_THREAD_POOL_NAME;
        }
        if (isConfiguration(threadPoolName)) {
            return SpringContextUtil.getValue(getKey(threadPoolName));
        }
        return threadPoolName;
    }

    /**
     * 获取线程池bean名称
     * @param threadPoolName 注解上的线程池名
     * @return 没有指定线程名使用默认线程池bean
     */
    private String getThreadPoolName(String threadPoolName) {
        if (StrUtil.isEmpty(threadPoolName)) {
            threadPoolName = DefaultThreadPool.THREAD_POOL_NAME;
        }
        if (isConfiguration(threadPoolName)) {
            return SpringContextUtil.getValue(getKey(threadPoolName));
        }
        return threadPoolName;
    }


    /**
     * 获取线程名称
     * @param threadName 线程名称
     * @return 线程名称,没有则使用默认线程名称
     */
    private String getThreadName(String threadName) {
        if (StrUtil.isEmpty(threadName)) {
            threadName = DefaultThreadPool.THREAD_NAME;
        }
        if (isConfiguration(threadName)) {
            return SpringContextUtil.getValue(getKey(threadName));
        }
        return threadName;
    }

    /**
     * 获取值
     * 加了${}就从配置文件中获取
     * 如果不是从配置文件取就用当前值
     * @param key 从配置文件获取值需要的key
     * @return 如果不是从配置文件取就用当前值,不是就用当前值
     */
    private String getValue(String key) {
        if (isConfiguration(key)) {
            return SpringContextUtil.getValue(getKey(key));
        }
        return key;
    }

    /**
     * 是否从配置文件获取
     * @param key key
     * @return true重配置文件获取
     */
    private boolean isConfiguration(String key) {
        return Pattern.matches("^\\$\\{[\\w.|-]*}$", key);
    }

    /**
     * 原始key改为从配置文件获取key
     * @param key key
     * @return 从配置文件获取的key
     */
    private String getKey(String key) {
        return key.replaceAll("[${}]", "");
    }

    /**
     * 获取使用cron表达式第一次运行时间
     * @return 第一次运行延迟时间
     */
    private long getCronInitialDelay(String cron) {
        String runTimeStr = DateUtil.today() + " " + resetCron(cron) + ":00";
        DateTime parse = DateUtil.parse(runTimeStr);
        long timeMillis = System.currentTimeMillis();
        if (parse.getTime() < timeMillis) {
            return parse.getTime() + StaticValues.DAY_MILLI_SECONDS - timeMillis;
        }
        return parse.getTime() - timeMillis;
    }

    /**
     * 将cron补0
     * 如0:30
     * 改为00::30
     * @param cron 需要装换的时间
     * @return 转换后的时间
     */
    private String resetCron(String cron) {
        String[] split = cron.split(StrUtil.COLON);
        return String.format("%02d", Integer.parseInt(split[0])) + StrUtil.COLON + String.format("%02d", Integer.parseInt(split[1]));
    }
}
